package com.tang.lts.core.nio.channel;

import com.tang.lts.core.nio.codec.Decoder;
import com.tang.lts.core.nio.codec.Encoder;
import com.tang.lts.core.nio.config.NioConfig;
import com.tang.lts.core.nio.config.NioServerConfig;
import com.tang.lts.core.nio.handler.Futures;
import com.tang.lts.core.nio.handler.NioHandler;
import com.tang.lts.core.nio.idle.IdleInfo;
import com.tang.lts.core.nio.idle.IdleState;
import com.tang.lts.core.nio.loop.NioSelectorLoop;
import com.tang.lts.core.nio.processor.NioProcessor;
import com.tang.lts.core.nio.processor.NioServerProcessor;

import java.io.IOException;
import java.net.SocketAddress;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SocketChannel;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author Tang
 * @classname NioChannelImpl
 * @description [  ]
 * @date 2022/5/15 15:34
 */
public class NioChannelImpl implements NioChannel {

    private NioProcessor processor;
    private NioSelectorLoop selectorLoop;
    protected SocketChannel channel;
    private NioHandler eventHandler;
    private NioConfig config;

    private final long id;
    private static final AtomicInteger CONN_ID = new AtomicInteger(0);
    private IdleInfo idleInfo;

    private Decoder decoder;
    private Encoder encoder;

    protected Futures.CloseFuture closeFuture = Futures.newCloseFuture();

    public NioChannelImpl(NioSelectorLoop selectorLoop, NioProcessor nioProcessor,
                          SocketChannel socketChannel, NioHandler eventHandler, NioConfig config) {
        this.channel = socketChannel;
        this.selectorLoop = selectorLoop;
        this.processor = nioProcessor;
        this.eventHandler = eventHandler;
        this.config = config;

        id = CONN_ID.incrementAndGet();
        closeFuture.setChannel(this);

        this.idleInfo = new IdleInfo();
    }

    @Override
    public Futures.WriteFuture writeFlush(Object msg) {
        return processor.writeFlush(this,msg);
    }

    public IdleInfo getIdleInfo() {
        return idleInfo;
    }

    public SelectableChannel javaChannel() {
        return this.channel;
    }

    public NioHandler eventHandler() {
        return eventHandler;
    }

    @Override
    public long getId() {
        return id;
    }

    @Override
    public SocketAddress remoteAddress() {
        return socketChannel().socket().getRemoteSocketAddress();
    }

    @Override
    public SocketAddress localAddress() {
        return socketChannel().socket().getLocalSocketAddress();
    }

    @Override
    public boolean isConnected() {
        return false;
    }

    @Override
    public boolean isOpen() {
        return false;
    }

    @Override
    public boolean isClosed() {
        return false;
    }

    @Override
    public SocketChannel socketChannel() {
        return this.channel;
    }

    @Override
    public void setLastReadTime(long lastReadTime) {
        idleInfo.setLastReadTime(lastReadTime);
    }

    @Override
    public void setLastWriteTime(long lastWriteTime) {
        idleInfo.setLastWriteTime(lastWriteTime);
    }

    @Override
    public Futures.CloseFuture close() {
        try {
            javaChannel().close();
            closeFuture.setSuccess(true);
        } catch (IOException e) {
            e.printStackTrace();
            closeFuture.setSuccess(true);
            closeFuture.setCause(e);
            // 回调错误
            eventHandler().exceptionCaught(this,e);
        }
        closeFuture.notifyListeners();
        return closeFuture;
    }

    public NioConfig getConfig() {
        return config;
    }

    public void fireChannel(IdleState idleState, long currentTime) {
        switch (idleState) {
            case BOTH_IDLE:
                idleInfo.setLastBothIdleTime(currentTime);
                break;
            case WRITER_IDLE:
                idleInfo.setLastWriteIdleTime(currentTime);
                break;
            case READER_IDLE:
                idleInfo.setLastReadIdleTime(currentTime);
                break;
            default:
                break;
        }
        eventHandler.channelIdle(this,idleState);
    }

    @Override
    public Decoder getDecoder() {
        return decoder;
    }

    public void setDecoder(Decoder decoder) {
        this.decoder = decoder;
    }

    @Override
    public Encoder getEncoder() {
        return encoder;
    }

    public void setEncoder(Encoder encoder) {
        this.encoder = encoder;
    }
}